#include "campaignform.h"
#include "ui_campaignform.h"
#include "dtwentyitemdelegate.h"

#include <campaign.h>
#include <xmlserializer.h>

#include <QDebug>
#include <QCoreApplication>
#include <QTimer>
#include <QHeaderView>
#include <QMessageBox>
#include <QFile>


CampaignForm::CampaignForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CampaignForm)
{
    qRegisterMetaType<Campaign*>();

    ui->setupUi(this);

    addAction(ui->actionDelete);

//    m_persistentActions << ui->actionDelete;

    // source models
    m_dtwentyModel = new DTwentyModel(this);
    m_ccModel = new CharacterClassTableModel(this);


    // tree view
    m_sortModel = new QSortFilterProxyModel(this);
    m_sortModel->setSourceModel(m_dtwentyModel);
    m_sortModel->setSortRole(DTwentyModel::Role_SORT);
    m_sortModel->sort(0);


    ui->treeView->setModel(m_sortModel);
    ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->treeView->setItemDelegate(new DTwentyItemDelegate(this));

    connect(ui->treeView->model(), &QAbstractItemModel::layoutChanged, this, &CampaignForm::onLayoutChanged);
    connect(ui->treeView->model(), &QAbstractItemModel::dataChanged, this, &CampaignForm::onRefreshActions);
    connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CampaignForm::onSelectionChanged);

    // table view

    QSortFilterProxyModel* ccSortModel = new QSortFilterProxyModel(this);
    ccSortModel->setSourceModel(m_ccModel);
    ccSortModel->setSortRole(Qt::DisplayRole);
    ccSortModel->sort(0);

    ui->tableView->setModel(ccSortModel);
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);

}


CampaignForm::~CampaignForm()
{
    delete ui;
}



void CampaignForm::create()
{
    Campaign* campaign = new Campaign(this);
    m_dtwentyModel->setRoot(campaign);

    QTimer::singleShot(0, m_dtwentyModel, SIGNAL(layoutAboutToBeChanged()));
    QTimer::singleShot(0, m_dtwentyModel, SIGNAL(layoutChanged()));
    QTimer::singleShot(0, this, SLOT(onRefreshActions()));

}

bool CampaignForm::open(const QString &fileName)
{
    if (fileName.isEmpty())
    {
        QMessageBox::warning(this, tr("File Name is Empty!"), tr("Can't open empty file name!"));
        return false;
    }

    m_fileName = fileName;
    QFile file(m_fileName);
    if (!file.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, tr("File Open Failed!"), tr("Can't open file!"));
        return false;
    }

    XmlSerializer serializer;
    Campaign* campaign = qobject_cast<Campaign*>(serializer.deserialize(&file));

    if (!campaign)
    {
        QMessageBox::warning(this, tr("Parsing Campaign Failed!"), tr("File is not a Campaign file!"));
        return false;
    }

    m_dtwentyModel->setRoot(campaign);

    QTimer::singleShot(0, m_dtwentyModel, SIGNAL(layoutAboutToBeChanged()));
    QTimer::singleShot(0, m_dtwentyModel, SIGNAL(layoutChanged()));
    QTimer::singleShot(0, this, SLOT(onRefreshActions()));

    return true;
}

bool CampaignForm::save(const QString &fileName)
{
    if (!fileName.isEmpty())
    {
        m_fileName = fileName;
    }

    if (m_fileName.isEmpty())
    {
        return false;
    }

    QFile file(m_fileName);
    if (!file.open(QFile::WriteOnly))
    {
        QMessageBox::warning(this, tr("File Save Failed!"), tr("Can't save file!"));
        return false;
    }

    XmlSerializer serializer;
    serializer.serialize(m_dtwentyModel->root(), &file);

    return true;
}

QFileInfo CampaignForm::fileInfo() const
{
    return QFileInfo(m_fileName);
}

QList<QAction *> CampaignForm::campaignActions()
{
    foreach (QAction* action, m_campaignActions)
    {
        action->deleteLater();
    }
    m_campaignActions.clear();

    QAbstractItemModel* model = ui->treeView->model();
    if (!model)
    {
        qDebug() << Q_FUNC_INFO << "Failed B";
        return QList<QAction *>();
    }


    QModelIndex start = model->index(0, 0, QModelIndex());
    if (!start.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Failed C";
        return QList<QAction *>();
    }

    QModelIndexList indexes = model->match(start,
                                           DTwentyModel::Role_DTWENTY_BASETYPE,
                                           DTwentyModel::DTwentyBaseType_CONTAINER,
                                           -1,
                                           Qt::MatchExactly);

    foreach (QModelIndex index, indexes)
    {
        QAction* action = new QAction(this);

        QString text = index.data(DTwentyModel::Role_CHILD_TYPENAME).toString();
        action->setText(text);
        action->setToolTip(QString("%1 %2").arg(tr("Create New")).arg(text));
        action->setStatusTip(QString("%1 %2").arg(tr("Create New")).arg(text));

        QVariant data = QVariant::fromValue(QPersistentModelIndex(index));
        action->setData(data);

        connect(action, SIGNAL(triggered()), this, SLOT(onModelActionTriggered()));
        m_campaignActions << action;
    }

    return m_campaignActions;
}

QMap<QString, QAction *> CampaignForm::characterClassActions()
{
    foreach (QAction* action, m_characterClassActions)
    {
        action->deleteLater();
    }
    m_characterClassActions.clear();

    int type = qMetaTypeId<CharacterClass*>();
    if (type == QMetaType::UnknownType)
    {
        qDebug() << Q_FUNC_INFO << "Failed A";
        return QMap<QString, QAction *>();
    }

    QAbstractItemModel* model = ui->treeView->model();
    if (!model)
    {
        qDebug() << Q_FUNC_INFO << "Failed B";
        return QMap<QString, QAction *>();
    }


    QModelIndex start = model->index(0, 0, QModelIndex());
    if (!start.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Failed C";
        return QMap<QString, QAction *>();
    }

    QModelIndexList searchIndexes = model->match(start,
                                           DTwentyModel::Role_TYPE,
                                           type,
                                           -1,
                                           Qt::MatchRecursive);

//    qDebug() << Q_FUNC_INFO << "Found" << searchIndexes.count() << "character classes";

    foreach (QModelIndex search, searchIndexes)
    {
        start = model->index(0, 0, search);
        QModelIndexList indexes = model->match(start,
                                               DTwentyModel::Role_DTWENTY_BASETYPE,
                                               DTwentyModel::DTwentyBaseType_CONTAINER,
                                               -1,
                                               Qt::MatchRecursive);

        qDebug() << Q_FUNC_INFO << "Found" << indexes.count() << "containers for" << search.data().toString();

        foreach (QModelIndex index, indexes)
        {
            QAction* action = new QAction(this);

            QString text = QString("Add to %1").arg(index.data().toString());
            action->setText(text);
            QString tipText = QString("Add %1 to %2")
                    .arg(index.data(DTwentyModel::Role_CHILD_TYPENAME).toString())
                    .arg(index.data().toString());

            action->setToolTip(tipText);
            action->setStatusTip(tipText);

            QVariant data = QVariant::fromValue(QPersistentModelIndex(index));
            action->setData(data);

            connect(action, SIGNAL(triggered()), this, SLOT(onModelActionTriggered()));
            m_characterClassActions.insertMulti(search.data().toString(), action);
        }

        qDebug() << "*********************************";
    }

//    qDebug() << Q_FUNC_INFO << "Action count:" << m_characterClassActions.count();

    return m_characterClassActions;
}

void CampaignForm::onRefreshActions()
{
//    foreach(QAction* action, actions())
//    {
//        removeAction(action);
//        if (m_persistentActions.contains(action))
//        {
//            continue;
//        }

//        action->setParent(0);
//        action->deleteLater();
//    }

//    QAbstractItemModel* model = ui->treeView->model();
//    if (!model)
//    {
//        return;
//    }


//    QModelIndex index = ui->treeView->currentIndex();
//    if (index.isValid())
//    {
//        ui->treeView->expand(index);
//    }

//    QModelIndex start = model->index(0, 0, QModelIndex());

//    QModelIndexList containers = model->match(start,
//                                              DTwentyModel::Role_DTWENTY_BASETYPE,
//                                              DTwentyModel::DTwentyBaseType_CONTAINER,
//                                              -1,
//                                              Qt::MatchRecursive);


//    static QString ACTION_PREFIX = tr("Add to");
//    foreach (QModelIndex container, containers)
//    {
//        QString text = QString("%1 %2")
//                .arg(ACTION_PREFIX)
//                .arg(container.data().toString());


//        QAction* action = new QAction(text, this);

//        QVariant data = QVariant::fromValue(QPersistentModelIndex(container));
//        action->setData(data);

//        connect(action, SIGNAL(triggered()), this, SLOT(onModelActionTriggered()));
//        addAction(action);
//    }

//    addAction(ui->actionDelete);


    emit sigActionsChanged();

}

void CampaignForm::onModelActionTriggered()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (!action || action == ui->actionDelete)
    {
        qDebug() << Q_FUNC_INFO << "Sender action is invalid!";
        return;
    }

    QPersistentModelIndex parent = qvariant_cast<QPersistentModelIndex>(action->data());
    if (!parent.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Sender action data is invalid model index!";
        return;
    }

    QAbstractItemModel* model = ui->treeView->model();
    int row = model->rowCount(parent);

    model->insertRows(row, 1, parent);
    ui->treeView->expand(parent);

    QModelIndex sourceParent = sourceIndex(parent);
    if (!sourceParent.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped source parent is invalid";
        return;
    }

    QModelIndex sourceIndex = sourceParent.child(row, 0);
    if (!sourceIndex.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Source index at" << row << "is invalid!";
        return;
    }


    QModelIndex index = m_sortModel->mapFromSource(sourceIndex); //parent.child(row, 0);
    if (!index.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped proxy index from inserted index is invalid";
        return;
    }

    ui->treeView->setCurrentIndex(index);
    ui->treeView->edit(index);
    ui->treeView->model()->sort(0);

    QTimer::singleShot(0, this, SLOT(onRefreshActions()));
}

void CampaignForm::onSelectionChanged()
{
    QModelIndex curr = ui->treeView->currentIndex();

    QStringList info;


    for (int role = ItemTreeModel::Role_FIRST; role <= DTwentyModel::Role_LAST; role++)
    {
        if (role == ItemTreeModel::Role_TYPE ||
                role == DTwentyModel::Role_CHILD_TYPE ||
                role == DTwentyModel::Role_DTWENTY_BASETYPE)
        {
            continue;
        }


        QVariant data = curr.data(role);
        if (data.isNull())
        {
            continue;
        }

        if (data.canConvert(QVariant::Map))
        {
            QStringList tmp;
            QVariantMap dataMap = data.toMap();
            foreach (QString dataKey, dataMap.keys())
            {
                tmp << QString("<li>%1</li>").arg(dataKey);
            }

            if (!tmp.isEmpty())
            {
                info << QString("<ol>%1</ol>").arg(tmp.join("\n"));
            }

            continue;
        }

        info << QString("<li>%1</li>").arg(data.toString());
    }

    QString text = QString("<h1>%1</h1>").arg(curr.data(Qt::DisplayRole).toString());
    if (!info.isEmpty())
    {
        text = text + QString("<ul>%1</ul>").arg(info.join("\n"));
    }

    ui->textEdit->setText(text);
}

void CampaignForm::onLayoutChanged()
{
    ui->treeView->hideColumn(1);

    QVariantMap characterClasses = m_dtwentyModel->characterClasses();
    if (characterClasses.isEmpty())
    {
        ui->comboBox->hide();
        ui->tableView->hide();
        return;
    }

    QString current = ui->comboBox->currentText();
    ui->comboBox->clear();

    foreach (QString name, characterClasses.keys())
    {
        ui->comboBox->addItem(name, characterClasses.value(name));
    }

    int index = ui->comboBox->findText(current);
    if (index < 0)
    {
        ui->comboBox->setCurrentIndex(0);
    }
    else
    {
        ui->comboBox->setCurrentIndex(index);
    }

    ui->comboBox->show();
    ui->tableView->show();
}


void CampaignForm::on_actionDelete_triggered()
{
    qDebug() << Q_FUNC_INFO;

    QModelIndex index = ui->treeView->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    ui->treeView->model()->removeRows(index.row(), 1, index.parent());

    QTimer::singleShot(0, this, SLOT(onRefreshActions()));
}


QModelIndex CampaignForm::sourceIndex(const QModelIndex &index) const
{
    const QAbstractProxyModel* proxyModel = qobject_cast<const QAbstractProxyModel*>(index.model());
    if (!proxyModel)
    {
        return index;
    }

    return sourceIndex(proxyModel->mapToSource(index));
}


void CampaignForm::on_comboBox_currentIndexChanged(int index)
{
    qDebug() << Q_FUNC_INFO;
    m_ccModel->setCharacterClass(qvariant_cast<CharacterClass*>(ui->comboBox->itemData(index)));
}
